package cn.zyjblogs.server.user.service.impl;

import cn.zyjblogs.server.user.constant.LoginEnum;
import cn.zyjblogs.server.user.dto.UserLoginDto;
import cn.zyjblogs.server.user.service.LoginService;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.constant.CommonRedisKeyConstant;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.exception.AuthRuntimeException;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler;
import cn.zyjblogs.starter.sms.common.SmsCommon;
import cn.zyjblogs.starter.sms.common.SmsEnum;
import cn.zyjblogs.starter.sms.common.SmsRequest;
import cn.zyjblogs.starter.sms.common.SmsResponse;
import cn.zyjblogs.starter.sms.exception.SmsRuntimeException;
import cn.zyjblogs.starter.sms.provider.SmsGranter;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author zhuyijun
 */
@Service
public class LoginServiceImpl implements LoginService {
    private final TokenGranter tokenGranter;
    private final ClientDetailsService clientDetails;
    private final OAuth2RequestFactory oAuth2RequestFactory;
    private final TokenStore tokenStore;
    private final SmsGranter smsGranter;
    private final RedisTemplateHandler<String, String> redisTemplateHandler;

    public LoginServiceImpl(AuthorizationServerEndpointsConfiguration authorizationServerEndpointsConfiguration,
                            ClientDetailsService clientDetails,
                            TokenStore tokenStore, SmsGranter smsGranter,
                            RedisTemplateHandler redisTemplateHandler) {
        this.tokenGranter = authorizationServerEndpointsConfiguration.getEndpointsConfigurer().getTokenGranter();
        this.clientDetails = clientDetails;
        this.oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetails);
        this.tokenStore = tokenStore;
        this.smsGranter = smsGranter;
        this.redisTemplateHandler = redisTemplateHandler;
    }

    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Override
    public OAuth2AccessTokenVo login(UserLoginDto userLoginDto) {
        Map<String, String> parameters = BeanUtils.map(userLoginDto, Map.class);
        parameters.put("username", userLoginDto.getUserIdentification());
        parameters.put("password", userLoginDto.getPwdOrVerifyCode());
        if (!StringUtils.hasLength(userLoginDto.getLoginType())) {
            throw new AuthRuntimeException(HttpCode.BAD_REQUEST, "登录类型不能为空");
        }
        parameters.put("grant_type", LoginEnum.getGrantTypeByCode(userLoginDto.getLoginType()));
        ClientDetails authenticatedClient = clientDetails.loadClientByClientId(clientId);
        if (authenticatedClient == null) {
            throw new AuthRuntimeException(HttpCode.INTERNAL_SERVER_ERROR, "客户端获取token失败");
        }
        try {
            TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(parameters, authenticatedClient);
            OAuth2AccessToken token = tokenGranter.grant(tokenRequest.getGrantType(), tokenRequest);
            return OAuth2AccessTokenVo.TransferToken(token);
        } catch (Exception e) {
            throw new AuthRuntimeException(HttpCode.INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

    @Override
    public void logout() {
        String token = BaseContext.getToken();
        if (StringUtils.isEmpty(token)) {
            return;
        }
        token = token.substring(HttpHeaderConstant.BEARER_TYPE.length()).trim();
        OAuth2AccessToken oAuth2AccessToken = new DefaultOAuth2AccessToken(token);
        tokenStore.removeAccessToken(oAuth2AccessToken);
    }

    @Override
    public SmsResponse sendLoginCode(String phone) {
        int smsCode = RandomUtils.nextInt(100000, 999999);
        String code = redisTemplateHandler.get(CommonRedisKeyConstant.SMS_LOGIN + phone);
        int count = 1;
        if (StringUtils.hasLength(code)) {
            int currentCount = Integer.parseInt(code.split(",")[1]);
            if (currentCount > 3) {
                throw new SmsRuntimeException("操作过于频繁，请稍后尝试");
            }
            count = currentCount + 1;
        }
        redisTemplateHandler.set(CommonRedisKeyConstant.SMS_LOGIN + phone, smsCode + "," + count, 5, TimeUnit.MINUTES);
        SmsRequest smsRequest = new SmsRequest();
        smsRequest.setSignName("逝水无痕博客");
        smsRequest.setTemplateCode("SMS_255200753");
        smsRequest.setParamsMap(Map.of(SmsCommon.TEMPLATE_PARAM, Map.of("code", smsCode)));
        return smsGranter.sendSms(SmsEnum.ALI_SMS, phone, smsRequest);
    }
}
